﻿/*
# -*- coding: utf-8 -*-
# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

# 注意：

# 若希望将本代码用于商业用途，需先联系作者并获得明确授权。
# 作者不对因使用本代码引发的任何直接或间接损失负责。
# 如有违反上述条款的行为，作者保留追究法律责任的权利。

# 免责声明：
# 请尊重知识产权，遵循相关法律法规。

# Notice:
# If you wish to use this code for commercial purposes, you must contact the author and obtain explicit authorization.
# The author is not responsible for any direct or indirect losses caused by the use of this code.
# If any of the above terms are violated, the author reserves the right to pursue legal action.
# Disclaimer:
# Please respect intellectual property rights and comply with relevant laws and regulations.

# Внимание:
# Если вы хотите использовать этот код в коммерческих целях, вам необходимо связаться с автором и получить явное разрешение.
# Автор не несет ответственности за любые прямые или косвенные убытки, возникшие в результате использования этого кода.
# В случае нарушения вышеуказанных условий автор оставляет за собой право на юридическое преследование.
# Отказ от ответственности:
# Пожалуйста, уважайте интеллектуальную собственность и соблюдайте соответствующие законы и нормативные акты.

# Attention :
# Si vous souhaitez utiliser ce code à des fins commerciales, vous devez contacter l'auteur et obtenir une autorisation explicite.
# L'auteur n'est pas responsable des pertes directes ou indirectes causées par l'utilisation de ce code.
# En cas de violation des conditions ci-dessus, l'auteur se réserve le droit de poursuivre une action en justice.
# Avertissement :
# Veuillez respecter les droits de propriété intellectuelle et vous conformer aux lois et réglementations en vigueur.

 */
using System;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;

public  class Board : MonoBehaviour,IInitializable
{
    public event Action<BoardTile> BoardTileClicked;

    [SerializeField] protected BoardTile boardTilePrefab;
    [SerializeField] protected BoardTile backgroundTilePrefab;
    [SerializeField] protected float _width;
    [SerializeField] protected int _rows;
    [SerializeField] protected float _spacing=0.05f;
    [SerializeField] protected bool debugCoordinate;
    [SerializeField] protected Piece piecePrefab;
    [SerializeField] protected bool hideBoardTiles = true;


    private readonly List<BoardTile> _boardTiles = new List<BoardTile>();

    public IEnumerable<BoardTile> BoardTiles => _boardTiles;

    public float TileSize { get; private set; }

    public  Color PieceColor { get; set; }
    
    public BoxGrid BoxGrid { get; private set; }
    public bool Intractable { get; set; } = true;

    public IEnumerable<Piece> Pieces => BoardTiles.Where(b => b.Tile != null).Select(b => (Piece) b.Tile);



    public BoardTile this[Vector2Int coordinate]
    {
        get
        {
            var coordinateFromBase = coordinate+new Vector2Int(_rows/2,_rows/2);
            return _boardTiles[coordinateFromBase.x*_rows+coordinateFromBase.y];
        }
    }




    public bool Initialized { get; protected set; }
    


    public void Init()
    {
        if (Initialized)
        {
            return;
        }
        UpdateBoard();

        Initialized = true;
    }

    protected virtual void Awake()
    {
        Init();
    }




    public void AddPiece(Piece piece, Vector2Int to)
    {
        piece.Holder = this[to];
        this[to].Tile = piece;
        piece.transform.position = this[to].transform.position;

    }

    public virtual void AddPiece(PieceData data)
    {
        var piece = Instantiate(piecePrefab);
        piece.Color = PieceColor;
        AddPiece(piece, data.coordinate);
        piece.Clicked += tile => OnBoardTileClicked((BoardTile)tile.Holder);
    }


    public void RemovePiece(Piece piece)
    {
        var holder =  piece.Holder as BoardTile;
        if (holder!=null && holder.Tile == piece)
        {
            holder.Tile = null;
        }
        Destroy(piece.gameObject);
    }

    public Vector2Int GetNearestCoordinate(Vector2 position)
    {
        return BoxGrid.GetNearestCoordinateForRelativePosition(position - (Vector2) transform.position);
    }


    public void MovePiece(Piece piece, Vector2Int to)
    {
        var holder =  piece.Holder as BoardTile;
        if (holder!=null && holder.Tile == piece)
        {
            holder.Tile = null;
        }

        this[to].Tile = piece;
        piece.transform.position = this[to].transform.position;
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(transform.position,new Vector3(_width,_width,0.1f));
    }

    [ContextMenu("Update Board")]
    public void UpdateBoard()
    {
        ResetBoard();
        TileSize = (_width - (_rows - 1) * _spacing) / _rows;
        BoxGrid = new BoxGrid(TileSize,_spacing);
        foreach (var boardTile in transform.GetComponentsInChildren<BoardTile>())
        {
            if (Application.isPlaying)
                Destroy(boardTile.gameObject);
            else
            {
                DestroyImmediate(boardTile.gameObject);
            }
        }

        _boardTiles.Clear();

        for (var i = -_rows / 2; i <= _rows / 2; i++)
        {
            for (var j = -_rows / 2; j <= _rows / 2; j++)
            {
                BoardTile tile=null;

                if (Application.isPlaying)
                {
                    tile = Instantiate(boardTilePrefab,
                        (Vector2)transform.position + BoxGrid.GetRelativePositionForCoordinate(new Vector2Int(i, j)),
                        Quaternion.identity);
                }
                else
                {
#if UNITY_EDITOR
                    tile = (BoardTile)PrefabUtility.InstantiatePrefab(boardTilePrefab);
                    
                    tile.transform.position = (Vector2)transform.position +
                                              BoxGrid.GetRelativePositionForCoordinate(new Vector2Int(i, j));
#endif
                }
                tile.Size = TileSize;
                tile.transform.parent = transform;
                tile.LocalCoordinate = new Vector2Int(i, j);
                tile.HideRenderer = hideBoardTiles;
                tile.Clicked += BoardTileOnClicked;
                _boardTiles.Add(tile);
            }
        }

        BoardTile.DebugCoordinate = debugCoordinate;
    }

    protected void Refresh()
    {

    }

    private void BoardTileOnClicked(Tile tile)
    {
        OnBoardTileClicked((BoardTile) tile);
        BoardTileClicked?.Invoke((BoardTile) tile);
    }

    protected virtual void OnBoardTileClicked(BoardTile tile)
    {
    }

    public virtual void ResetBoard()
    {
        foreach (var boardTile in BoardTiles)
        {
           
                if (boardTile.Tile != null)
                {
                    Destroy(boardTile.Tile.gameObject);
                }

            boardTile.Tile = null;
        }
    }
}